all repos — caroster @ 512757d6ae90dfc3ddae08bb34ddb889ee3ec54f

[Octree] Group carpool to your event https://caroster.io

frontend/pages/e/[uuid]/details.tsx (view raw)

  1import moment from 'moment';
  2import Button from '@material-ui/core/Button';
  3import Box from '@material-ui/core/Box';
  4import Link from '@material-ui/core/Link';
  5import Paper from '@material-ui/core/Paper';
  6import Divider from '@material-ui/core/Divider';
  7import Container from '@material-ui/core/Container';
  8import TextField from '@material-ui/core/TextField';
  9import Typography from '@material-ui/core/Typography';
 10import {makeStyles} from '@material-ui/core/styles';
 11import {DatePicker} from '@material-ui/pickers';
 12import {PropsWithChildren, useState} from 'react';
 13import {useTranslation} from 'react-i18next';
 14import ShareEvent from '../../../containers/ShareEvent';
 15import useEventStore from '../../../stores/useEventStore';
 16import useToastStore from '../../../stores/useToastStore';
 17import useSettings from '../../../hooks/useSettings';
 18import EventLayout, {TabComponent} from '../../../layouts/Event';
 19import {
 20  EventByUuidDocument,
 21  useUpdateEventMutation,
 22} from '../../../generated/graphql';
 23import pageUtils from '../../../lib/pageUtils';
 24
 25interface Props {
 26  eventUUID: string;
 27  announcement?: string;
 28}
 29
 30const Page = (props: PropsWithChildren<Props>) => {
 31  return <EventLayout {...props} Tab={DetailsTab} />;
 32};
 33
 34const DetailsTab: TabComponent = ({}) => {
 35  const {t} = useTranslation();
 36  const settings = useSettings();
 37  const [updateEvent] = useUpdateEventMutation();
 38  const addToast = useToastStore(s => s.addToast);
 39  const setEventUpdate = useEventStore(s => s.setEventUpdate);
 40  const event = useEventStore(s => s.event);
 41  const [isEditing, setIsEditing] = useState(false);
 42  const idPrefix = isEditing ? 'EditEvent' : 'Event';
 43  const classes = useStyles();
 44
 45  const onSave = async e => {
 46    try {
 47      const {uuid, ...data} = event;
 48      const {id, travels, waitingPassengers, __typename, ...input} = data;
 49      await updateEvent({
 50        variables: {uuid, eventUpdate: input},
 51        refetchQueries: ['eventByUUID'],
 52      });
 53      setIsEditing(false);
 54    } catch (error) {
 55      console.error(error);
 56      addToast(t('event.errors.cant_update'));
 57    }
 58  };
 59
 60  const modifyButton = isEditing ? (
 61    <Button
 62      variant="contained"
 63      color="primary"
 64      className={classes.modify}
 65      onClick={onSave}
 66    >
 67      {t('event.details.save')}
 68    </Button>
 69  ) : (
 70    <Button
 71      variant="text"
 72      color="primary"
 73      className={classes.modify}
 74      onClick={() => setIsEditing(true)}
 75    >
 76      {t('event.details.modify')}
 77    </Button>
 78  );
 79
 80  if (!event) return null;
 81
 82  return (
 83    <Box className={classes.root}>
 84      <Container maxWidth="sm" className={classes.card}>
 85        <Paper className={classes.paper}>
 86          {modifyButton}
 87          <div className={classes.section}>
 88            <Typography variant="h6">{t('event.fields.name')}</Typography>
 89            {isEditing ? (
 90              <TextField
 91                fullWidth
 92                value={event.name}
 93                onChange={e => setEventUpdate({name: e.target.value})}
 94                name="name"
 95                id="EditEventName"
 96              />
 97            ) : (
 98              <Typography variant="body1" id={`${idPrefix}Name`}>
 99                {event.name ?? t('event.fields.empty')}
100              </Typography>
101            )}
102          </div>
103          <div className={classes.section}>
104            <Typography variant="h6">{t('event.fields.date')}</Typography>
105            {isEditing ? (
106              <DatePicker
107                fullWidth
108                placeholder={t('event.fields.date_placeholder')}
109                value={event.date}
110                onChange={date =>
111                  setEventUpdate({
112                    date: !date ? null : moment(date).format('YYYY-MM-DD'),
113                  })
114                }
115                format="DD/MM/YYYY"
116                cancelLabel={t('generic.cancel')}
117                clearable
118                clearLabel={t('generic.clear')}
119                id={`${idPrefix}Date`}
120              />
121            ) : (
122              <Typography variant="body1" id={`${idPrefix}Date`}>
123                {event.date
124                  ? moment(event.date).format('DD/MM/YYYY')
125                  : t('event.fields.empty')}
126              </Typography>
127            )}
128          </div>
129          <div className={classes.section}>
130            <Typography variant="h6">{t('event.fields.address')}</Typography>
131            {isEditing ? (
132              <TextField
133                fullWidth
134                multiline
135                rowsMax={4}
136                inputProps={{maxLength: 250}}
137                helperText={`${event.address?.length ?? 0}/250`}
138                defaultValue={event.address}
139                value={event.address}
140                onChange={e => setEventUpdate({address: e.target.value})}
141                id={`${idPrefix}Address`}
142                name="address"
143              />
144            ) : (
145              <Typography variant="body1" id={`${idPrefix}Address`}>
146                {event.address ? (
147                  <Link
148                    target="_blank"
149                    rel="noreferrer"
150                    href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
151                      event.address
152                    )}`}
153                    onClick={e => e.preventDefault}
154                  >
155                    {event.address}
156                  </Link>
157                ) : (
158                  t('event.fields.empty')
159                )}
160              </Typography>
161            )}
162          </div>
163          <div className={classes.section}>
164            <Typography variant="h6">
165              {t('event.fields.description')}
166            </Typography>
167            {isEditing ? (
168              <TextField
169                fullWidth
170                multiline
171                rowsMax={4}
172                inputProps={{maxLength: 250}}
173                helperText={`${event.description?.length || 0}/250`}
174                defaultValue={event.description}
175                value={event.description || ''}
176                onChange={e => setEventUpdate({description: e.target.value})}
177                id={`${idPrefix}Description`}
178                name="description"
179              />
180            ) : (
181              <Typography variant="body1" id={`${idPrefix}Description`}>
182                {event.description ?? t('event.fields.empty')}
183              </Typography>
184            )}
185          </div>
186          <Typography variant="h6">{t('event.fields.link')}</Typography>
187          <Typography>{t('event.fields.link_desc')}</Typography>
188          <Box py={4} justifyContent="center" display="flex">
189            <ShareEvent
190              title={`Caroster ${event.name}`}
191              url={`${window.location.href}`}
192            />{' '}
193          </Box>
194          <Divider variant="middle" />
195          <Box pt={2} justifyContent="center" display="flex">
196            <Link href={settings?.about_link} target="_blank" rel="noopener">
197              {t('event.details.aboutCaroster')}
198            </Link>
199          </Box>
200        </Paper>
201      </Container>
202    </Box>
203  );
204};
205
206const useStyles = makeStyles(theme => ({
207  root: {
208    position: 'relative',
209    paddingLeft: '80px',
210
211    [theme.breakpoints.down('sm')]: {
212      paddingLeft: 0,
213      paddingBottom: '80px',
214    },
215  },
216  paper: {
217    position: 'relative',
218    padding: theme.spacing(2),
219  },
220  card: {
221    marginTop: theme.spacing(6),
222  },
223  modify: {
224    position: 'absolute',
225    right: theme.spacing(2),
226  },
227  section: {
228    marginBottom: theme.spacing(2),
229    width: '540px',
230    maxWidth: '100%',
231    paddingX: theme.spacing(2),
232  },
233  map: {
234    marginTop: theme.spacing(4),
235  },
236  seeOnGMapButton: {
237    marginLeft: theme.spacing(2),
238  },
239}));
240
241export const getServerSideProps = pageUtils.getServerSideProps(
242  async (context, apolloClient) => {
243    const {uuid} = context.query;
244    const {host = ''} = context.req.headers;
245    let event = null;
246
247    // Fetch event
248    try {
249      const {data} = await apolloClient.query({
250        query: EventByUuidDocument,
251        variables: {uuid},
252      });
253      event = data?.eventByUUID?.data;
254    } catch (error) {
255      return {
256        notFound: true,
257      };
258    }
259
260    return {
261      props: {
262        eventUUID: uuid,
263        metas: {
264          title: event?.attributes?.name || '',
265          url: `https://${host}${context.resolvedUrl}`,
266        },
267      },
268    };
269  }
270);
271export default Page;